using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.Mappings;
using System.Configuration;
using Microsoft.Practices.RepositoryFactory.SchemaDiscovery.ObjectModel.Base;
using Microsoft.Practices.RepositoryFactory.CustomPages.Helpers;
using System.Data;

namespace Microsoft.Practices.RepositoryFactory.CustomPages.CreateRepositoryPages
{
   public class MapResultSetPagePresenter : Presenter<IMapResultSetPage>
   {
      private Entity entity;
      private EntityStoredProcedureMapping mapping;
      private ConnectionStringSettings connectionStringSettings;

      public void OnPageLoad()
      {
         if (mapping.ParameterMappings.Count < 1)
         {
            TryAutomapProperties();
         }

         RefreshDropDowns();
      }

      private void TryAutomapProperties()
      {
         // Only retrieve the stored procedure parameters if there is
         // at least one entity property
         List<Parameter> storedProcedureColumns = null;
         if (entity.Properties.Count > 0)
            storedProcedureColumns = GetStoredProcedureColumns();
         else
            return;

         foreach (Property property in entity.Properties)
         {
            foreach (Parameter parameter in storedProcedureColumns)
            {
               if (!IsParameterMapped(parameter))
               {
                  if (IsMappableFrom(property, parameter))
                  {
                     PropertyParameterMapping parameterMapping = new PropertyParameterMapping(property.Name, UIHelper.CloneParameter(parameter));
                     mapping.ParameterMappings.Add(parameterMapping);
                  }
               }
            }
         }
      }

      private bool IsMappableFrom(Property property, Parameter parameter)
      {
          return UIHelper.IsMappableFrom(property, parameter);
      }

      private void RefreshDropDowns()
      {
         List<Property> properties = GetNotMappedProperties();
         View.FillEntityProperties(properties);

         List<Parameter> parameters = GetNotMappedParameters();
         View.FillStoredProcedureOutputColumn(parameters);

         View.FillExistingMappings(mapping.ParameterMappings);
      }

      /// <summary>
      /// Gets a list of stored procedure parameters that are not mapped
      /// so as to present them in the user interface for mapping selection.
      /// </summary>
      /// <remarks>
      /// Cursors are excluded from the list of parameters because
      /// it should not be displayed in the UI.  Without this check, Oracle 
      /// REF CURSORS would be listed for users to map (undesirable).
      /// </remarks>
      /// <returns>A list of stored procedure parameters.</returns>
      private List<Parameter> GetNotMappedParameters()
      {
         // this gets ALL stored proc params
         List<Parameter> storedProcedureparameters = GetStoredProcedureColumns();

         List<Parameter> parameters = new List<Parameter>();
         foreach (Parameter parameter in storedProcedureparameters)
         {
            if (!IsParameterMapped(parameter))
            {
               if ((parameter.DbDataType.ProviderType == DatabaseType.Cursor) ||
                   (parameter.DbDataType.ProviderType == DatabaseType.Object))
               {
                  this.AddCursorMapping(parameter);
               }
               else
               {
                  parameters.Add(parameter);
               }
            }
         }

         return parameters;
      }

      private List<Parameter> GetStoredProcedureColumns()
      {
         StoredProcedure procedure = UIHelper.GetStoredProcedure(mapping.Procedure, connectionStringSettings);
         List<Parameter> storedProcedureparameters = UIHelper.GetParameters(connectionStringSettings, procedure, System.Data.ParameterDirection.ReturnValue);
         return this.CleanUpStoredProcedureParameterList(storedProcedureparameters);
      }

      /// <summary>
      /// The purpose of this method is to attach parameters to the appropriate cursor 
      /// and to remove parameters not needed.
      /// </summary>
      /// <remarks>
      /// In Oracle, strongly typed cursors may use a record type; this creates 
      /// a parameter type for the record type (with a blank name) and is removed 
      /// by this method.
      /// IMPORTANT ASSUMPTION: In a package specification, cursor parameters 
      /// are always placed last (after scalar parameters).
      /// </remarks>
      /// <param name="storedProcedureParameters"></param>
      /// <returns></returns>
      private List<Parameter> CleanUpStoredProcedureParameterList (List<Parameter> storedProcedureParameters)
      {
         string cursorName = String.Empty;
         List<Parameter> workingStoredProcedureParameterList = new List<Parameter>();

         foreach (Parameter parameter in storedProcedureParameters)
         {
            // do not copy Oracle record types as they are not true stored
            // procedure parameters
            if (parameter.DbDataType.ProviderType != DatabaseType.NotSupported)
            {
               // grab the name of the cursor to prefix all subsequent
               // parameters with the cursor name until the next cursor
               if (parameter.DbDataType.ProviderType == DatabaseType.Cursor)
               {
                  cursorName = parameter.DBName;
                  workingStoredProcedureParameterList.Add(parameter);
               }
               if (parameter.DbDataType.ProviderType != DatabaseType.Cursor)
               {
                  if (!String.IsNullOrEmpty(cursorName))
                     parameter.DBName = cursorName + "." + parameter.DBName;
                  workingStoredProcedureParameterList.Add(parameter);
               }
            }
         }
         return workingStoredProcedureParameterList;
      }

      private List<Property> GetNotMappedProperties()
      {
         List<Property> properties = new List<Property>();

         foreach (Property property in entity.Properties)
         {
            if (!IsPropertyMapped(property))
            {
               properties.Add(property);
            }
         }

         return properties;
      }

      private bool IsPropertyMapped(Property property)
      {
         foreach (PropertyParameterMapping parameterMapping in mapping.ParameterMappings)
         {
            if (string.Compare(parameterMapping.Property, property.Name, true) == 0)
            {
               return true;
            }
         }

         return false;
      }

      private bool IsParameterMapped(Parameter parameter)
      {
         foreach (PropertyParameterMapping parameterMapping in mapping.ParameterMappings)
         {
            if (string.Compare(parameterMapping.Parameter, parameter.Name, true) == 0)
            {
               return true;
            }
         }

         return false;
      }

      /// <summary>
      /// This method maps REF CURSORS to a non-existent property with the same cursor name
      /// </summary>
      /// <param name="parameter"></param>
      private void AddCursorMapping(Parameter parameter)
      {
         PropertyParameterMapping parameterMapping = new PropertyParameterMapping(
            UIHelper.CloneParameter(parameter));
         parameterMapping.Property = parameter.DBName;
         mapping.ParameterMappings.Add(parameterMapping);
      }


      public MapResultSetPagePresenter(Entity entity, EntityStoredProcedureMapping mapping,
            ConnectionStringSettings connectionStringSettings)
      {
         this.entity = entity;
         this.mapping = mapping;
         this.connectionStringSettings = connectionStringSettings;
      }


      public void OnAddMapping(Parameter parameter, Property property)
      {
         PropertyParameterMapping parameterMapping = new PropertyParameterMapping(property.Name,
             UIHelper.CloneParameter(parameter));

         mapping.ParameterMappings.Add(parameterMapping);

         View.UpdateDropDowns(parameter, property, parameterMapping);
      }

      public void OnRemoveMapping(PropertyParameterMapping propertyMapping)
      {
         mapping.ParameterMappings.Remove(propertyMapping);

         RefreshDropDowns();
      }

      public void OnComplete()
      {
         Identity identity = new Identity();
         identity.Name = mapping.OperationName + "Identity";

         StoredProcedure procedure = UIHelper.GetStoredProcedure(mapping.Procedure, connectionStringSettings);
         List<Parameter> inputParameters = UIHelper.GetParameters(connectionStringSettings, procedure, ParameterDirection.Input);
         inputParameters.AddRange(UIHelper.GetParameters(connectionStringSettings, procedure, ParameterDirection.InputOutput));

         foreach (Parameter identityParameter in inputParameters)
         {
            identity.ParameterMappings.Add(new PropertyParameterMapping(identityParameter.Name,
                                                                        UIHelper.CloneParameter(identityParameter)));
         }

         mapping.Identity = identity;
      }
   }
}
